#include "maze_game.h"
#include "genetic_utils.h"

mazeGame::mazeGame(mazeMap* map, int popSize, int genomeSize, int maxgenerations, double crossoverRate, double mutationRate)
	:geneticAlg(crossoverRate, mutationRate),
	mMap(map),
	mPopSize(popSize),
	mGenomeSize(genomeSize),
	mMaxGenerations(maxgenerations)
{
	//todo something
	initializePop();
}

mazeGame::~mazeGame()
{
	//todo something
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void mazeGame::initializePop()
{
	for (int i = 0; i < mPopSize; i++)
	{
		genome<int> chromo;

		for (int j = 0; j < mGenomeSize; j++)
		{
			chromo.push_back(RandInt(0, 3)); /** generate random number range 0 - 3 */
		}

		mPop.push_back(chromo);
	}
}

void mazeGame::geneMutate(int gene, int geneLength, double mutationRate)
{
	int i;

	for (i = 0; i < geneLength; i++)
	{
		//do we flip this bit?
		if (RandFloat() < mutationRate)
		{
			//flip the bit
			gene ^= ((1 << (i - 1)));
		}
	}
}

void mazeGame::mutate(genome<int>& chromo)
{
	for (size_t i = 0; i < chromo.size(); i++)
	{
		geneMutate(chromo[i], 2, mMutationRate);
	}//next bit
}

void mazeGame::genomeDecode(genome<int>& chromo, std::vector<int>& moveSteps)
{
	for (size_t i = 0; i < chromo.size(); i++)
	{
		moveSteps.push_back(chromo[i]);
	}
}

double mazeGame::testPlay(std::vector<int>& moveSteps)
{
	int endX = 0;
	int endY = 0;
	int outX = 0;
	int outY = 0;

	mMap->getInputCoordinate(&endX, &endY);
	mMap->getOutputCoordinate(&outX, &outY);

	mMap->testRun(moveSteps, &endX, &endY);

	//now we know the finish point of Bobs journey, let's assign
	//a fitness score which is proportional to his distance from
	//the exit

	int	diff_x = abs(endX - outX);
	int diff_y = abs(endY - outY);

	//we add the one to ensure we never divide by zero. Therefore
	//a solution has been found when this return value = 1
	return 1 / (double)(diff_x + diff_y + 1);
}

void mazeGame::updateFitnessScore()
{
	int fittest_genome = 0;
	double best_fitness_score = 0;
	double total_fitness_score = 0;


	//update the fitness scores and keep a check on fittest so far
	for (int i = 0; i < mPopSize; ++i)
	{
		genome<int>& chromo = mPop[i];
		vector<int> moveSteps;
		//decode each genomes chromosome into a vector of directions
		//genomeDecode(chromo, moveSteps);

		//get it's fitness score
		chromo.mFitness = testPlay(chromo);


		//update total
		total_fitness_score += chromo.mFitness;

		//if this is the fittest genome found so far, store results
		if (chromo.mFitness > best_fitness_score)
		{
			best_fitness_score = chromo.mFitness;

			fittest_genome = i;

			//Has we found the exit of maze?
			if (chromo.mFitness == 1)
			{
				//is so, stop the run
				mIsRunning = false;
			}
		}

	}//next genome

	mFittestGenomeIdx = fittest_genome;
	mBestFitnessScore = best_fitness_score;
	mTotalFitnessScore = total_fitness_score;
}

void mazeGame::epoch(std::vector<genome<int>>& oldPop, std::vector<genome<int>>& newPop)
{
	updateFitnessScore();

	if (mIsRunning)
	{
		geneticAlg<int>::epoch(oldPop, newPop);
		//increment the generation counter
		mGenerationCnt++;
	}
}

void mazeGame::play()
{
	std::vector<genome<int>> newPop;
	mIsRunning = true;

	while (mGenerationCnt < mMaxGenerations)
	{
		newPop.clear();

		epoch(mPop, newPop);


		if (mIsRunning)
		{
			mPop = newPop;
		}
		else
		{
			if (newPop.size() == 0)
			{
				mMap->run(mPop[mFittestGenomeIdx]);
			}
			else
			{
				mMap->run(newPop[mFittestGenomeIdx]);
			}
			
			break;
		}
	}
	
	if (mIsRunning)
	{
		printf("not found fittest genome\n");		
	}
	printf("genetic generation: %d\n", mGenerationCnt);
}